﻿using System;
using System.Globalization;
using Microsoft.International.Formatters;
using System.Reflection;

[assembly: CLSCompliant(true)]
namespace Microsoft.International
{

    /// <summary>
    /// Provides a formatter class that converts the numbers in the numeric data types to a string with local numeric representation in FIGSR languages.
    /// </summary>
    internal class EuropeNumericFormatter : IFormatProvider, ICustomFormatter
    {

        /// <summary>
        /// It returns an object that implements ICustomFormatter to do the formatting.
        /// </summary>
        /// <param name="formatType">Format type.</param>
        /// <returns>
        /// Returns the same object if the type of <see cref="T:System.ICustomFormatter" /> is the same as the parameter formatType .
        /// Returns null if the type of <see cref="T:System.ICustomFormatter" /> is different from the parameter formatType.
        /// </returns>
        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter))
                return this;

            return null;
        }

        /// <summary>
        /// Formats the object into representation of specified FIGSR culture.
        /// </summary>
        /// <param name="format">Format type.</param>
        /// <param name="arg">Number to be formatted.</param>
        /// <param name="formatProvider">Format provider.</param>
        /// <returns>Localized string in FIGSR forms.</returns>
        /// <exception cref="T:System.ArgumentNullException">
        /// If the format parameter of <see cref="M:Microsoft.International.LocalNumericFormatter.FormatWithCulture(System.String,System.Object,System.IFormatProvider,System.Globalization.CultureInfo)" /> method is null, <see cref="T:System.ArgumentNullException" /> will be thrown.
        /// </exception>
        /// <exception cref="T:System.ArgumentNullException">
        /// If the arg parameter of <see cref="M:Microsoft.International.LocalNumericFormatter.FormatWithCulture(System.String,System.Object,System.IFormatProvider,System.Globalization.CultureInfo)" /> method is null, <see cref="T:System.ArgumentNullException" /> will be thrown.
        /// </exception>
        /// <exception cref="T:System.ArgumentNullException">
        /// If the culture parameter of <see cref="M:Microsoft.International.LocalNumericFormatter.FormatWithCulture(System.String,System.Object,System.IFormatProvider,System.Globalization.CultureInfo)" /> method is null, <see cref="T:System.ArgumentNullException" /> will be thrown.
        /// </exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// If the number is out of range, <see cref="T:System.ArgumentOutOfRangeException" /> will be thrown.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// Invalid argument type, only built-in number type is supported, <see cref="T:System.ArgumentException" /> will be thrown.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// Invalid combination of parameter culture and format, <see cref="T:System.ArgumentException" /> will be thrown.
        /// </exception>
        public String Format(String format, Object arg, IFormatProvider formatProvider)
        {
            // Raise an exception if format is null
            if (format == null)
                throw new ArgumentNullException("format");

            // Raise an exception if arg is null
            if (arg == null)
                throw new ArgumentNullException("arg");

            // If the culture is not a FormatCultureInfo
            // we use the current culture of the thread
            CultureInfo formatCulture = formatProvider as FormatCultureInfo;
            if (formatCulture == null)
                formatCulture = CultureInfo.CurrentCulture;

            // Check the input format
            if (!format.Equals("L"))
            {
                IFormattable formattable = arg as IFormattable;
                if (formattable == null)
                    return arg.ToString();

                return formattable.ToString(format, formatProvider);
            }

            // Get the correct formatter for the specified culture
            EuropeFormatter formatter = GetFormatter(formatCulture);
            if (formatter == null)
                throw new ArgumentException("The specified format is not supported in this culture.");

            Type type = arg.GetType();
            if ((((((type != typeof(Double)) && (type != typeof(Single))) && ((type != typeof(Int32)) && (type != typeof(UInt32)))) && (((type != typeof(long)) && (type != typeof(ulong))) && ((type != typeof(short)) && (type != typeof(ushort))))) && ((type != typeof(sbyte)) && (type != typeof(byte)))) && (type != typeof(decimal)))
                throw new ArgumentException("Invalid argument type. Use a integer type.", "arg");

            Double num = Convert.ToDouble(arg, null);
            if (formatter.CheckOutOfRange(num))
                throw new ArgumentOutOfRangeException("arg");

            return formatter.ConvertToLocalizedText(Convert.ToDecimal(arg, null));
        }

        /// <summary>
        /// Return the correct formatter for the specified culture
        /// </summary>
        /// <param name="culture">The specified culture</param>
        /// <returns>Return the formatter for the specified culture</returns>
        private static EuropeFormatter GetFormatter(CultureInfo culture)
        {
            // Load the assembly Microsoft.International
            Assembly assembly = Assembly.Load("Microsoft.International");

            // For each types into the assembly Microsoft.International
            foreach (Type type in assembly.GetTypes())
                // The type must inherits from the class EuropeFormatter
                if (type.BaseType == typeof(EuropeFormatter))
                    // Load all FormatCultureAttribute attributes
                    foreach (FormatCultureInfoAttribute attribute in type.GetCustomAttributes(typeof(FormatCultureInfoAttribute), false))
                        if (attribute.CultureName == culture.Name)
                            // Create an instance for the correct formatter
                            return Activator.CreateInstance(type) as EuropeFormatter;

            // The correct formatter have not been found
            return null;
        }

    }
}